//* ////////////////////////////////////////////////////////////////////////////// */
//*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//        Copyright (c) 2005-2013 Intel Corporation. All Rights Reserved.
//
//
//*/

#ifndef __SAMPLE_PLUGIN_H__
#define __SAMPLE_PLUGIN_H__

#include <stdlib.h>
#include <memory>

#include "mfx_plugin_base.h"
#include "rotate_plugin_api.h"
#include "sample_defs.h"

#include "opencl_filter.h"

// msvc ignores throw function specifications used in cl.hpp
#pragma warning(disable : 4290)
#define __CL_ENABLE_EXCEPTIONS

#define _interlockedbittestandset      fake_set
#define _interlockedbittestandreset    fake_reset
#define _interlockedbittestandset64    fake_set64
#define _interlockedbittestandreset64  fake_reset64
#include <intrin.h>
#include "CL/cl.hpp"
#undef _interlockedbittestandset
#undef _interlockedbittestandreset
#undef _interlockedbittestandset64
#undef _interlockedbittestandreset64

typedef struct {
    mfxU32 StartLine;
    mfxU32 EndLine;
} DataChunk;

class Processor
{
public:
    Processor();
    virtual ~Processor();
    virtual mfxStatus SetAllocator(mfxFrameAllocator *pAlloc);
    virtual mfxStatus Init(mfxFrameSurface1 *frame_in, mfxFrameSurface1 *frame_out);
    virtual mfxStatus Process(DataChunk *chunk) = 0;

protected:
    //locks frame or report of an error
    mfxStatus LockFrame(mfxFrameSurface1 *frame);
    mfxStatus UnlockFrame(mfxFrameSurface1 *frame);
    

    mfxFrameSurface1  *m_pIn;
    mfxFrameSurface1  *m_pOut;
    mfxFrameAllocator *m_pAlloc;
};

class Rotator180 : public Processor
{
public:
    Rotator180();
    virtual ~Rotator180();
    
    virtual mfxStatus Process(DataChunk *chunk);
};

inline cl::size_t<1> make_size_t(const size_t &v0) {
    cl::size_t<1> ret;
    ret[0] = v0;
    return ret;
}
inline cl::size_t<2> make_size_t(const size_t &v0, const size_t &v1) {
    cl::size_t<2> ret;
    ret[0] = v0;
    ret[1] = v1;
    return ret;
}
inline cl::size_t<3> make_size_t(const size_t &v0, const size_t &v1, const size_t &v2) {
    cl::size_t<3> ret;
    ret[0] = v0;
    ret[1] = v1;
    ret[2] = v2;
    return ret;
}


class OpenCLFilterRotator180 : public Processor
{
public:
    OpenCLFilterRotator180(OpenCLFilter *pOpenCLFilter);
    virtual ~OpenCLFilterRotator180();
    
    virtual mfxStatus Process(DataChunk * /* chunk */); // operates on whole frame
private:
    OpenCLFilter *m_pOpenCLFilter;
};

class OpenCLRotator180Context
{
public:
    OpenCLRotator180Context(const std::string &filename);

    void Rotate(size_t width, size_t height, 
                size_t pitchIn, size_t pitchOut,
                void *pInY,  void *pInUV,
                void *pOutY, void *pOutUV);
private:
    inline void CreateBuffers(const cl::size_t<3> &Y_size,
                              const cl::size_t<3> &UV_size);
    inline void SetKernelArgs();

    cl::Platform m_platform;
    cl::Program m_program;
    cl::Device m_device;
    cl::Context m_context;
    cl::CommandQueue m_queue;
    cl::Kernel m_kernelY;
    cl::Kernel m_kernelUV;

    cl::Image2D m_InY;
    cl::Image2D m_InUV;
    cl::Image2D m_OutY;
    cl::Image2D m_OutUV;
};

class OpenCLRotator180 : public Processor
{
public:
    OpenCLRotator180(OpenCLRotator180Context *pOpenCLRotator180Context);
    virtual ~OpenCLRotator180();
    
    virtual mfxStatus Process(DataChunk * /* chunk */); // operates on whole frame
private:
    OpenCLRotator180Context *m_pOpenCLRotator180Context;
};

typedef struct {
    mfxFrameSurface1 *In;
    mfxFrameSurface1 *Out;
    bool bBusy;    
    Processor *pProcessor;    
} RotateTask;

class Rotate : public MFXPluginBase
{
public:
    Rotate();
    virtual ~Rotate();      

    // methods to be called by Media SDK
    virtual mfxStatus mfxPluginInit(mfxCoreInterface *core);
    virtual mfxStatus mfxPluginClose();
    virtual mfxStatus mfxGetPluginParam(mfxPluginParam *par);
    virtual mfxStatus mfxSubmit(const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task);
    virtual mfxStatus mfxExecute(mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a);
    virtual mfxStatus mfxFreeResources(mfxThreadTask task, mfxStatus sts); 

    // methods to be called by application    
    virtual mfxStatus QueryIOSurf(mfxVideoParam *par, mfxFrameAllocRequest request[2]);
    virtual mfxStatus SetAllocator(mfxFrameAllocator *pAlloc);
    virtual mfxStatus SetHandle(mfxHandleType type, mfxHDL handle);
    virtual mfxStatus Init(mfxVideoParam *mfxParam, void* auxParam, int auxParamSize);
    virtual mfxStatus Close(); 
  
protected:
    bool m_bInited;    

    bool m_bOpenCLSurfaceSharing;
    OpenCLFilter m_OpenCLFilter;
    std::auto_ptr<OpenCLRotator180Context> m_pOpenCLRotator180Context;

    IDirect3DDeviceManager9 *m_pD3D9Manager;

    mfxCoreInterface *m_pmfxCore;

    mfxVideoParam   m_VideoParam;
    mfxPluginParam  m_PluginParam;
    RotateParam     m_Param;

    RotateTask      *m_pTasks;
    mfxU32          m_MaxNumTasks;

    mfxFrameAllocator * m_pAlloc;
    DataChunk *m_pChunks;

    mfxU32 m_NumChunks;

    mfxStatus CheckParam(mfxVideoParam *mfxParam, RotateParam *pRotatePar);
    mfxStatus CheckInOutFrameInfo(mfxFrameInfo *pIn, mfxFrameInfo *pOut);
    mfxU32 FindFreeTaskIdx();

    bool m_bIsInOpaque;
    bool m_bIsOutOpaque;
};

#endif __SAMPLE_PLUGIN_H__
